package com.yupi.springbootinit.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yupi.springbootinit.common.ErrorCode;
import com.yupi.springbootinit.common.ResultUtils;
import com.yupi.springbootinit.constant.CommonConstant;
import com.yupi.springbootinit.esdao.PostEsDao;
import com.yupi.springbootinit.exception.BusinessException;
import com.yupi.springbootinit.exception.ThrowUtils;
import com.yupi.springbootinit.mapper.PostMapper;
import com.yupi.springbootinit.model.dto.post.PostEsDTO;
import com.yupi.springbootinit.model.dto.post.PostQueryRequest;
import com.yupi.springbootinit.model.entity.Comment;
import com.yupi.springbootinit.model.entity.Post;
import com.yupi.springbootinit.model.entity.User;
import com.yupi.springbootinit.model.vo.PostVO;
import com.yupi.springbootinit.model.vo.UserVO;
import com.yupi.springbootinit.service.CommentRqService;
import com.yupi.springbootinit.service.CommentService;
import com.yupi.springbootinit.service.PostService;
import com.yupi.springbootinit.service.UserService;
import com.yupi.springbootinit.utils.SqlUtils;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;

import com.yupi.springbootinit.utils.ThumbNailUtil;
import lombok.extern.slf4j.Slf4j;
import cn.hutool.core.collection.CollUtil;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.sort.SortBuilder;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

/**
 * 帖子服务实现
 **/
@Service
@Slf4j
public class PostServiceImpl extends ServiceImpl<PostMapper, Post> implements PostService {

    @Resource
    private UserService userService;

    @Resource
    private PostMapper postMapper;


    @Resource
    private ElasticsearchRestTemplate elasticsearchRestTemplate;

    @Override
    public void validPost(Post post, boolean add) {
        if (post == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        String title = post.getTitle();
        String content = post.getContent();
        String tags = post.getTags();
        // 创建时，参数不能为空
        if (add) {
            ThrowUtils.throwIf(StringUtils.isAnyBlank(title, content, tags), ErrorCode.PARAMS_ERROR);
        }
        // 有参数则校验
        if (StringUtils.isNotBlank(title) && title.length() > 80) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "标题过长");
        }
        if (StringUtils.isNotBlank(content) && content.length() > 8192) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "内容过长");
        }
        List<String> sensitiveWords = Arrays.asList(// 违反法律法规类
                "法轮功", "全能神", "华藏宗门", "冰毒", "海洛因", "大麻", "制毒", "贩毒", "P2P暴雷", "虚拟货币挖矿", "民间标会倒会", "套路贷",
                // 危害国家安全与社会稳定类
                "港独", "台独", "蒙独", "疆独", "反动会道门", "恶意诋毁国家政策", "抹黑医保改革", "唱衰国企",
                // 低俗色情类
                "阴茎", "睾丸", "阴道", "阴蒂", "做爱", "性交", "口交", "肛交", "自慰", "一夜情", "嫖娼", "约炮", "乳沟", "翘臀", "丝袜诱惑",
                "苍井空", "波多野结衣", "小泽玛利亚", "武藤兰", "AV女优", "成人影片", "三级片", "黄片", "色情直播", "裸聊", "露点", "性感内衣秀",
                "性癖好", "性虐待", "SM", "淫荡", "风骚", "狐狸精", "荡妇", "牛郎", "鸭店", "色情按摩", "情趣用品",
                "AV", "ML", "FJ", "KJ", "约P", "PC", "黄播", "色Q",
                // 暴力恐怖类
                "基地组织", "伊斯兰国", "博科圣地", "斩首", "肢解", "枪杀", "纵火", "爆炸", "绑架勒索", "持械抢劫",
                // 网络诈骗类
                "刷单返现骗局", "网络兼职诈骗", "冒充公检法诈骗", "交友杀猪盘", "荐股诈骗", "跑分", "赌博", "彩票",
                // 知识产权侵权类
                "山寨苹果手机", "假耐克运动鞋", "盗版微软系统", "未经授权翻拍某电影", "盗版某热门小说", "盗录某演唱会音频",
                // 人身攻击与歧视类
                "黑鬼", "白皮猪", "男尊女卑", "女权癌", "娘炮", "弱女子", "瘸子", "瞎子", "聋子", "弱智",
                // 不实信息与谣言类
                "某地发生9级地震", "某明星去世", "吃绿豆治百病", "喝醋软化血管", "永动机研发成功", "量子速读提升学习能力",
                // 广告垃圾信息类
                "特效抗癌药", "根治糖尿病神药", "一周瘦十斤减肥药", "三个月长高10厘米增高药", "加微信免费领礼品", "点击链接获取巨额奖金"); // 示例敏感词列表
        if (containsSensitiveWords(content, sensitiveWords)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "内容包含敏感词");
        }

    }

    /**
     * 判断文本是否包含敏感词
     * @param text
     * @param sensitiveWords
     * @return
     */
    private boolean containsSensitiveWords(String text, List<String> sensitiveWords) {
        if (StringUtils.isBlank(text)) {
            return false;
        }
        for (String word : sensitiveWords) {
            if (text.contains(word)) {
                return true;
            }
        }
        return false;
    }

    @Override
    public PostVO getPostVO(Post post, HttpServletRequest request) {
        PostVO postVO = PostVO.objToVo(post);
        long postId = post.getId();
        // 1. 关联查询用户信息
        Long userId = post.getUserId();
        User user = null;
        if (userId != null && userId > 0) {
            user = userService.getById(userId);
        }
        UserVO userVO = userService.getUserVO(user);
        postVO.setUser(userVO);

        return postVO;
    }

    @Value("${postFilePath}")
    private String postImgPath;

    @Override
    public List<String> uploadImage(List<MultipartFile> files) {
        List<String> fileNames = new ArrayList<>();

        for (MultipartFile file : files) {
            String imageName = file.getOriginalFilename();
            if (imageName == null || imageName.isEmpty()) {
                throw new BusinessException(ErrorCode.PARAMS_ERROR);
            }

            String[] split = imageName.split("\\.");
            String imgType = split[split.length - 1];
            UUID randomUUID = UUID.randomUUID();
            String fileName = randomUUID + "." + imgType;
            String filePath = postImgPath + fileName;
            ThumbNailUtil.thumbnail(file, filePath,imgType);
            fileNames.add(fileName);
        }

        return fileNames;
    }

    @Override
    public boolean deletePostImage(String fileName) {
        String[] filenames = StringUtils.split(",", fileName);
        for ( String filename: filenames) {
            String filePath = postImgPath + filename;

            File imageFile = new File(filePath);
            if (imageFile.exists()) {
                boolean success = imageFile.delete();
                if(!success) {
                    throw  new BusinessException(ErrorCode.SYSTEM_ERROR,"删除图片失败");
                }
            } else {
                throw  new BusinessException(ErrorCode.PARAMS_ERROR,"图片不存在");
            }
        }
        return true;
    }

    @Resource
    private CommentService commentService;

    @Resource
    private CommentRqService commentRqService;
    @Override
    public boolean deleteOldPost() {
        LocalDateTime threeMonthAgo = LocalDateTime.now().minusMonths(3);
        QueryWrapper<Post> queryWrapper = new QueryWrapper<>();
        queryWrapper.lt("createTime", threeMonthAgo);
        List<Post> posts = postMapper.selectList(queryWrapper);
        if (posts.isEmpty()) {
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "没有过期的贴子");
        }
        //这里是删除帖子，并删除贴子随带的所有评论以及图片
        for (Post post : posts) {
            Long id = post.getId();
            commentService.deleteOldComment(id);
            commentRqService.deleteOldCommentRq(id);
            String imgpath = post.getImgpath();
            if(StringUtils.isNotBlank(imgpath)){
                this.deletePostImage(imgpath);
            }

            int i = postMapper.deleteOldPostById(id);
            if (i == 0) {
                throw new BusinessException(ErrorCode.OPERATION_ERROR, "删除帖子失败");
            }
        }
        return true;


    }

    @Override
    public Page<Post> searchFromEs(PostQueryRequest postQueryRequest) {
        Long id = postQueryRequest.getId();
        Long notId = postQueryRequest.getNotId();
        String searchText = postQueryRequest.getSearchText();
        String title = postQueryRequest.getTitle();
        String content = postQueryRequest.getContent();
        List<String> tagList = postQueryRequest.getTags();
        List<String> orTagList = postQueryRequest.getOrTags();
        Long userId = postQueryRequest.getUserId();
        // es 起始页为 0
        long current = postQueryRequest.getCurrent() - 1;
        long pageSize = postQueryRequest.getPageSize();
        String sortField = postQueryRequest.getSortField();
        String sortOrder = postQueryRequest.getSortOrder();
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        // 过滤
        boolQueryBuilder.filter(QueryBuilders.termQuery("isDelete", 0));
        if (id != null) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("id", id));
        }
        if (notId != null) {
            boolQueryBuilder.mustNot(QueryBuilders.termQuery("id", notId));
        }
        if (userId != null) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("userId", userId));
        }
        // 必须包含所有标签
        if (CollUtil.isNotEmpty(tagList)) {
            for (String tag : tagList) {
                boolQueryBuilder.filter(QueryBuilders.termQuery("tags", tag));
            }
        }
        // 包含任何一个标签即可
        if (CollUtil.isNotEmpty(orTagList)) {
            BoolQueryBuilder orTagBoolQueryBuilder = QueryBuilders.boolQuery();
            for (String tag : orTagList) {
                orTagBoolQueryBuilder.should(QueryBuilders.termQuery("tags", tag));
            }
            orTagBoolQueryBuilder.minimumShouldMatch(1);
            boolQueryBuilder.filter(orTagBoolQueryBuilder);
        }
        // 按关键词检索
        if (StringUtils.isNotBlank(searchText)) {
            boolQueryBuilder.should(QueryBuilders.matchQuery("title", searchText));
            boolQueryBuilder.should(QueryBuilders.matchQuery("description", searchText));
            boolQueryBuilder.should(QueryBuilders.matchQuery("content", searchText));
            boolQueryBuilder.minimumShouldMatch(1);
        }
        // 按标题检索
        if (StringUtils.isNotBlank(title)) {
            boolQueryBuilder.should(QueryBuilders.matchQuery("title", title));
            boolQueryBuilder.minimumShouldMatch(1);
        }
        // 按内容检索
        if (StringUtils.isNotBlank(content)) {
            boolQueryBuilder.should(QueryBuilders.matchQuery("content", content));
            boolQueryBuilder.minimumShouldMatch(1);
        }
        // 排序
        SortBuilder<?> sortBuilder = SortBuilders.scoreSort();
        if (StringUtils.isNotBlank(sortField)) {
            sortBuilder = SortBuilders.fieldSort(sortField);
            sortBuilder.order(CommonConstant.SORT_ORDER_ASC.equals(sortOrder) ? SortOrder.ASC : SortOrder.DESC);
        }
        // 分页
        PageRequest pageRequest = PageRequest.of((int) current, (int) pageSize);
        // 构造查询
        NativeSearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(boolQueryBuilder)
                .withPageable(pageRequest).withSorts(sortBuilder).build();
        SearchHits<PostEsDTO> searchHits = elasticsearchRestTemplate.search(searchQuery, PostEsDTO.class);
        Page<Post> page = new Page<>();
        page.setTotal(searchHits.getTotalHits());
        List<Post> resourceList = new ArrayList<>();
        // 查出结果后，从 db 获取最新动态数据（比如点赞数）
        if (searchHits.hasSearchHits()) {
            List<SearchHit<PostEsDTO>> searchHitList = searchHits.getSearchHits();
            List<Long> postIdList = searchHitList.stream().map(searchHit -> searchHit.getContent().getId())
                    .collect(Collectors.toList());
            List<Post> postList = baseMapper.selectBatchIds(postIdList);
            if (postList != null) {
                Map<Long, List<Post>> idPostMap = postList.stream().collect(Collectors.groupingBy(Post::getId));
                postIdList.forEach(postId -> {
                    if (idPostMap.containsKey(postId)) {
                        resourceList.add(idPostMap.get(postId).get(0));
                    } else {
                        // 从 es 清空 db 已物理删除的数据
                        String delete = elasticsearchRestTemplate.delete(String.valueOf(postId), PostEsDTO.class);
                        log.info("delete post {}", delete);
                    }
                });
            }
        }
        page.setRecords(resourceList);
        return page;
    }



}




